﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;
using System.Net;
using Microsoft.SharePoint.Publishing;
using System.Xml;
using Microsoft.SharePoint.Utilities;

namespace Macaw.Moss2007.XmlSitemapSolution
{
    public class XmlSitemapJobDefinition : SPJobDefinition
    {

        private void Init()
        {
            this.Id = JobId;
            this.Title = "XmlSitemap Job";
            SPDailySchedule schedule = new SPDailySchedule();
            this.Schedule = schedule;
        }

        public XmlSitemapJobDefinition()
            : base()
        {
            Init();
        }

        public XmlSitemapJobDefinition(SPWebApplication webapplication)
            : base(JobName, webapplication, null, SPJobLockType.Job)
        {
            Init();
        }

        public static string JobName = "XmlSitemapJob";
        public static Guid JobId = new Guid("{FE1F1447-6549-454a-B32E-51BF80B7F178}");

        public XmlSitemapJobDefinition(string jobName, SPService service, SPServer server, SPJobLockType targetType)
            : base(jobName, service, server, SPJobLockType.None)
        {
            Init();
        }

        public XmlSitemapJobDefinition(string jobName, SPWebApplication webApplication)
            : base(jobName, webApplication, null, SPJobLockType.None)
        {
            Init();
        }

        private const string SITEMAPNAMESPACE = "http://www.sitemaps.org/schemas/sitemap/0.9";

        protected void AddPublishingPageToSitemap(PublishingPage page, PublishingWeb web, XmlDocument sitemapdocument, XmlElement urlset, SPAlternateUrl alturl)
        {
            XmlElement url = (XmlElement)urlset.AppendChild(sitemapdocument.CreateElement("url", SITEMAPNAMESPACE));

            XmlElement loc = (XmlElement)url.AppendChild(sitemapdocument.CreateElement("loc", SITEMAPNAMESPACE));
            loc.InnerText = SPUrlUtility.CombineUrl(alturl.IncomingUrl, page.ListItem.File.ServerRelativeUrl);

            XmlElement lastmod = (XmlElement)url.AppendChild(sitemapdocument.CreateElement("lastmod", SITEMAPNAMESPACE));
            lastmod.InnerText = page.LastModifiedDate.ToString("yyyy-MM-dd");

            if (page.ListItem.Versions.Count > 1)
            {
                SPListItemVersion liv_last = page.ListItem.Versions[page.ListItem.Versions.Count - 1];
                SPListItemVersion liv_first = page.ListItem.Versions[0];
                TimeSpan ts = liv_first.Created.Subtract(liv_last.Created);
                double avghours = ts.TotalHours / page.ListItem.Versions.Count;

                string changefreqvalue = null;

                if (avghours >= 0 && avghours <= 24)
                {
                    changefreqvalue = "daily";
                }
                else if (avghours > 24 & avghours <= 7 * 24)
                {
                    changefreqvalue = "weekly";
                }
                else if (avghours > 7 * 24 && avghours <= 365 * 24)
                {
                    changefreqvalue = "monthly";
                }
                else
                {
                    changefreqvalue = "yearly";
                }
                if (!string.IsNullOrEmpty(changefreqvalue))
                {
                    XmlElement changefreq = (XmlElement)url.AppendChild(sitemapdocument.CreateElement("changefreq", SITEMAPNAMESPACE));
                    changefreq.InnerText = changefreqvalue;
                }
            }

            XmlElement priority = (XmlElement)url.AppendChild(sitemapdocument.CreateElement("priority", SITEMAPNAMESPACE));
            if (web.DefaultPage.UniqueId.Equals(page.ListItem.UniqueId))
            {
                priority.InnerText = "1.0";
            }
            else
            {
                priority.InnerText = "0.5";
            }
        }

        protected void AddSitemapDocumentToSitemapStorage(XmlDocument sitemapdocument, SPSite site, XmlDocument indexdocument, XmlElement sitemapindex, XmlSitemapStorage sitemapstorage, SPAlternateUrl alturl)
        {
            #region Save Sitemap file
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            XmlTextWriter xtw = new XmlTextWriter(ms, Encoding.UTF8);
            xtw.Formatting = Formatting.Indented;
            sitemapdocument.Save(xtw);
            xtw.Flush();

            ms.Seek(0, System.IO.SeekOrigin.Begin);

            string sitemapname = string.Format("sitemap.{0}.xml", sitemapstorage.Sitemaps.Count);
            System.IO.StreamReader sitemapstreamreader = new System.IO.StreamReader(ms);
            sitemapstorage.Sitemaps.Add(sitemapname, sitemapstreamreader.ReadToEnd());
            ms.Close();
            #endregion

            #region Add saved Sitemap to SitemapIndex
            XmlElement index_sitemap = (XmlElement)sitemapindex.AppendChild(indexdocument.CreateElement("sitemap", SITEMAPNAMESPACE));
            XmlElement index_loc = (XmlElement)index_sitemap.AppendChild(indexdocument.CreateElement("loc", SITEMAPNAMESPACE));
            XmlElement index_lastmod = (XmlElement)index_sitemap.AppendChild(indexdocument.CreateElement("lastmod", SITEMAPNAMESPACE));
            
            index_loc.InnerText = SPUrlUtility.CombineUrl(alturl.IncomingUrl, sitemapname);
            index_lastmod.InnerText = DateTime.Now.ToString("yyyy-MM-dd");
            #endregion
        }

        private const int MAXPAGESPERSITEMAP = 100;

        public override void Execute(Guid targetInstanceId)
        {
            SPWebApplication webApplication = this.WebApplication;

            XmlSitemapStorageCollection storagecollection = new XmlSitemapStorageCollection(webApplication);

            foreach (SPSite siteCollection in this.WebApplication.Sites)
            {
                for (int idx = webApplication.AlternateUrls.Count - 1; idx >= 0; idx--)
                {
                    SPAlternateUrl aurl = webApplication.AlternateUrls[idx];

                    XmlDocument indexdocument = new XmlDocument();
                    XmlElement sitemapindex = (XmlElement)indexdocument.AppendChild(indexdocument.CreateElement("sitemapindex", SITEMAPNAMESPACE));

                    #region Setup storage
                    string SitemapStorageName = string.Format("XmlSitemap_{0}_{1}", siteCollection.ID, aurl.IncomingUrl);

                    
                    XmlSitemapStorage sitemapstorage = storagecollection.GetValue<XmlSitemapStorage>(SitemapStorageName);
                    //XmlSitemapStorage sitemapstorage2 = webApplication.GetChild<XmlSitemapStorage>(StorageId);
                    if (sitemapstorage == null)
                    {                        
                        sitemapstorage = new XmlSitemapStorage(SitemapStorageName, SPContext.Current.Site.WebApplication, Guid.NewGuid() );
                        storagecollection.Add(sitemapstorage);
                    }

                    sitemapstorage.SitemapIndex = null;
                    sitemapstorage.Sitemaps = new Dictionary<string, string>();
                    #endregion


                    #region Process Site Collection
                    using (SPSite site = new SPSite(siteCollection.ID, aurl.UrlZone, siteCollection.SystemAccount.UserToken))
                    {
                        XmlDocument sitemapdocument = null;
                        XmlElement urlset = null;
                        foreach (SPWeb web in site.AllWebs)
                        {
                            if (web.AllowAnonymousAccess)
                            {
                                if (PublishingWeb.IsPublishingWeb(web))
                                {
                                    PublishingWeb pweb = PublishingWeb.GetPublishingWeb(web);
                                    if (!pweb.PagesList.NoCrawl)
                                    {
                                        foreach (PublishingPage ppage in pweb.GetPublishingPages())
                                        {
                                            if (sitemapdocument == null)
                                            {
                                                sitemapdocument = new XmlDocument();
                                                urlset = (XmlElement)sitemapdocument.AppendChild(sitemapdocument.CreateElement("urlset", SITEMAPNAMESPACE));
                                            }
                                            AddPublishingPageToSitemap(ppage, pweb, sitemapdocument, urlset, aurl);
                                            if (urlset.ChildNodes.Count >= MAXPAGESPERSITEMAP)
                                            {
                                                AddSitemapDocumentToSitemapStorage(sitemapdocument, site, indexdocument, sitemapindex, sitemapstorage, aurl);
                                                sitemapdocument = null;
                                            }

                                        }
                                    }
                                }
                            }
                        }

                        if (sitemapdocument != null)
                        {
                            AddSitemapDocumentToSitemapStorage(sitemapdocument, site, indexdocument, sitemapindex, sitemapstorage, aurl);
                        }
                    }
#endregion
                    
                    #region Save SitemapIndex
                    System.IO.MemoryStream indexdocumentstream = new System.IO.MemoryStream();
                    XmlTextWriter indexdocumentstreamwriter = new XmlTextWriter(indexdocumentstream, Encoding.UTF8);
                    indexdocumentstreamwriter.Formatting = Formatting.Indented;
                    indexdocument.Save(indexdocumentstreamwriter);
                    indexdocumentstreamwriter.Flush();

                    indexdocumentstream.Seek(0, System.IO.SeekOrigin.Begin);
                    System.IO.StreamReader indexdocumentstreamreader = new System.IO.StreamReader(indexdocumentstream);                    
                    sitemapstorage.SitemapIndex = indexdocumentstreamreader.ReadToEnd();
                    #endregion
                    sitemapstorage.Update();
                }
            }
            base.Execute(targetInstanceId);
        }
    }
}
